package com.itcast.peft.service;

import java.time.LocalDateTime;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
import java.util.stream.Stream;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.itcast.peft.config.MultipleModelConfig.ChatModelHolder;
import com.itcast.peft.entity.InteractionQuestion;
import com.itcast.peft.entity.InteractionReply;
import com.itcast.peft.mapper.InteractionQuestionMapper;
import com.itcast.peft.mapper.InteractionReplyMapper;
import com.itcast.peft.util.AsyncWrapper;
import com.itcast.peft.util.ExceptionWrapper;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
public class InteractionService {

    @Value("${interaction.generation.factor:5}")
    private int generationFactor;
    
    @Resource
    private ChatModelHolder userprefGenerationModelHolder;

    private ChatClient generationClient;

    @Autowired
    private InteractionQuestionMapper interactionQuestionMapper;

    @Autowired
    private InteractionReplyMapper interactionReplyMapper;

    @Autowired
    private AsyncWrapper asyncWrapper;


    
    private ObjectMapper objectMapper = new ObjectMapper();

    @PostConstruct
    void init() {
        this.generationClient = ChatClient.builder(userprefGenerationModelHolder.getModel()).build();
        objectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.registerModule(new JavaTimeModule());
    }

    /**
     * 用AI生成虚拟数据
     */
    @Async
    public void dataGenerate() {
        var questionFutureList = Stream
                .<Supplier<List<InteractionQuestion>>>of(
                        () -> generateQuestion("请给出50个有关线性代数的问题，模拟学生提问的语气，不用写序号，使用中文输出"),
                        () -> generateQuestion("请给出50个有关计算机基础的问题，模拟学生提问的语气，不用写序号，使用中文输出"),
                        () -> generateQuestion("请给出50个有计算机组成原理的问题，模拟学生提问的语气，不用写序号，使用中文输出"),
                        () -> generateQuestion("请给出50个有关编译原理基础的问题，模拟学生提问的语气，不用写序号，使用中文输出"),
                        () -> generateQuestion("请给出50个有关计算机网络的问题，模拟学生提问的语气，不用写序号，使用中文输出"),
                        () -> generateQuestion("请给出50个有关C语言的问题，模拟学生提问的语气，不用写序号，使用中文输出"),
                        () -> generateQuestion("请给出50个有关Java的问题，模拟学生提问的语气，不用写序号，使用中文输出"),
                        () -> generateQuestion("请给出50个有关Python的问题，模拟学生提问的语气，不用写序号，使用中文输出"))
                .map(s -> CompletableFuture.supplyAsync(s, asyncWrapper.getExecutor())).toList();

        var answerFutureList = questionFutureList.stream().map(ExceptionWrapper.wrapToRTE(CompletableFuture::get))
                .flatMap(List::stream)
                .map(q -> CompletableFuture.runAsync(() -> generateAnswer(q), asyncWrapper.getExecutor())).toList();
        answerFutureList
                .forEach(ExceptionWrapper.<CompletableFuture<Void>, Void>wrapToRTE(CompletableFuture::get)::apply);
        log.info("interaction question/answer generated:{}", userprefGenerationModelHolder.getConfig().getModelName());
    }

    private List<InteractionQuestion> generateQuestion(String prompt) {
        var result = new LinkedList<InteractionQuestion>();
        Stream.iterate(0, i -> i + 1).limit(generationFactor).forEach(i -> {
            try {
                var opt = OpenAiChatOptions.builder().temperature(0.7d).build();
                var p = new Prompt(prompt + "\n你需要返回一个合法的JsonArray格式", opt);
                var lst = generationClient.prompt(p).call().entity(new ParameterizedTypeReference<List<String>>() {
                });
                var qlist = lst.stream().map(s -> {
                    InteractionQuestion question = new InteractionQuestion();
                    question.setTitle(s);
                    question.setDescription(s);
                    question.setAnswerTimes(2);
                    question.setCreateTime(LocalDateTime.now());
                    question.setUpdateTime(LocalDateTime.now());
                    return question;
                }).toList();
                interactionQuestionMapper.insert(qlist);
                log.info("interactionQuestion inserted:{}", qlist.size());
                result.addAll(qlist);
            } catch (Exception e) {
                log.warn("ignore invalid format.");
            }
        });
        return result;
    }

    private void generateAnswer(InteractionQuestion question) {
        var random = new Random();
        var base = random.nextInt(90, 90 * 20);
        // 1.生成默认风格
        var defContent = generationClient.prompt().user(question.getDescription()).call().content();
        var defReply = new InteractionReply();
        defReply.setContent(defContent);
        defReply.setQuestionId(question.getId());
        defReply.setLikedTimes((int) (base * 0.2));
        defReply.setAnswerId(0L);
        defReply.setCreateTime(LocalDateTime.now());
        defReply.setUpdateTime(LocalDateTime.now());
        interactionReplyMapper.insert(defReply);
        // 2.生成带有鼓励语气的风格
        var opt = OpenAiChatOptions.builder().temperature(0.7d).build();
        var p = new Prompt("请用充满热情和鼓励的语气回答，使用感叹词和积极形容词，避免中立表述。例如：'太棒了！你的想法很有创意！加油！'等，问题内容：" + question.getDescription()
                + "\n你需要返回一个合法的JsonArray格式", opt);

        var optContent = generationClient.prompt(p).call().content();
        var optReply = new InteractionReply();
        optReply.setContent(optContent);
        optReply.setQuestionId(question.getId());
        optReply.setLikedTimes((int) (base * 0.8)); // 假设用户偏好
        defReply.setAnswerId(0L);
        optReply.setCreateTime(LocalDateTime.now());
        optReply.setUpdateTime(LocalDateTime.now());
        interactionReplyMapper.insert(optReply);

        log.info("reply generated");
    }

    /**
     * 导入数据
     */
    public void importQuestion(String question) {
        try {
            var list = objectMapper.readValue(question, new TypeReference<List<InteractionQuestion>>() {});
            interactionQuestionMapper.insert(list);
            log.info("data inserted:{}", list.size());
        } catch (JsonProcessingException e) {
            log.error("error in parsing interaction question.");
        }
    }
    
    /**
     * 导入数据
     */
    public void importReply(String reply) {
        try {
            var list = objectMapper.readValue(reply, new TypeReference<List<InteractionReply>>() {});
            interactionReplyMapper.insert(list);
            log.info("data inserted:{}", list.size());
        } catch (JsonProcessingException e) {
            log.error("error in parsing interaction reply.");
        }
    }

}
